//
//  Repository.swift
//  ProjectApp
//
//  Created by 邹琳 on 2019/7/24.
//  Copyright © 2019 邹琳. All rights reserved.
//

import Foundation
import Alamofire

protocol Repository: class {
    
    var baseURL: URL { get }
    
    var commonParams: [String: Any] { get }
    
    func encode(resource: Resource) throws -> URLRequest
    
    func request<T: Decodable>(resource: Resource, completionHandler: @escaping (_ response: Swift.Result<T,NSError>) -> Void)
    
    func decode<T: Decodable>(data: Data) throws -> T
}

extension NSError {
    
    class func network() -> NSError {
        let reason = "网络错误"
        let code = -1000
        let userInfo = [NSLocalizedDescriptionKey: reason,
                        NSLocalizedFailureReasonErrorKey: reason]
        return NSError(domain: "com.app.networkerror", code: code, userInfo: userInfo)
    }
    
    class func dataSerialization() -> NSError {
        let reason = "数据解析错误"
        let code = -1001
        let userInfo = [NSLocalizedDescriptionKey: reason,
                        NSLocalizedFailureReasonErrorKey: reason]
        return NSError(domain: "com.app.datakerror", code: code, userInfo: userInfo)
    }
    
    class func backend(reason: String, code: Int) -> NSError {
        let userInfo = [NSLocalizedDescriptionKey: reason,
                        NSLocalizedFailureReasonErrorKey: reason]
        return NSError(domain: "com.app.backenderror", code: code, userInfo: userInfo)
    }
    
}

struct Resource {
    
    let method: HTTPMethod
    let path: String
    let params: [String: Any]?
    let headers: [String: Any]?
    let hostURL: URL
    
    init(method:HTTPMethod = .get, path: String, params:[String: Any]? = nil, headers: [String: Any]? = nil, hostURL: URL = HOSTURL) {
        self.method = method
        self.path = path
        self.params = params
        self.headers = headers
        self.hostURL = hostURL
    }
}

extension Repository {
    
    func encode(resource: Resource) throws -> URLRequest {
        var newparams = resource.params ?? [:]
        for (key, value) in commonParams {
            newparams[key] = value
        }
        
        let url = URL(string: resource.path, relativeTo: resource.hostURL)!
        var request = URLRequest(url: url)
        request.timeoutInterval = 15
        request.httpMethod = resource.method.rawValue
        let encoding = resource.method == .get ? URLEncoding.queryString : URLEncoding.httpBody
        do {
            let newRequest = try encoding.encode(request, with: newparams)
            return newRequest
        } catch {
            throw error
        }
    }
    
    func request<T: Decodable>(resource: Resource, completionHandler: @escaping (_ response: Swift.Result<T,NSError>) -> Void) {
        do {
            let request = try self.encode(resource: resource)
            Alamofire.request(request).responseData { (response) in
                switch response.result {
                case .success(let value):
                    do {
                        let result: T = try self.decode(data: value)
                        completionHandler(.success(result))
                    } catch {
                        completionHandler(.failure(NSError.dataSerialization()))
                    }
                case .failure:
                    completionHandler(.failure(response.error as NSError? ?? NSError.network()))
                }
            }
        } catch {
            completionHandler(.failure(NSError.network()))
        }
    }
}


